﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;

namespace Windows.Storage
{
    [TestClass]
    public class FileIOTempStorageTests
    {
        [ClassInitialize]
        public static async Task InitClass(TestContext context)
        {
            // copy our test files into isolated storage
            await FileHelper.CopyToTemp("file1.utf8.txt");
            await FileHelper.CopyToTemp("file1.utf8.nobom.txt");
            await FileHelper.CopyToTemp("file1.utf16le.txt");
            await FileHelper.CopyToTemp("file1.utf16le.nobom.txt");
            await FileHelper.CopyToTemp("file1.utf16be.txt");
            await FileHelper.CopyToTemp("file1.utf16be.nobom.txt");
        }

        [TestMethod]
        public async Task ReadTextAsyncTest()
        {
            // basic file reading text, in all encoding types
            string utf8 = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile());
            string utf16le = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile());
            string utf16be = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile());
            Assert.AreEqual(utf8, utf16le);
            Assert.AreEqual(utf8, utf16be);
        }

        [TestMethod]
        public async Task ReadTextWithEncodingAsyncTest()
        {
            // reference file
            string utf8 = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile());

            // FileIO detects the actual UTF8 encoding, ignores the provided flag
            string utf8a = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile(), UnicodeEncoding.Utf16BE);
            string utf8b = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile(), UnicodeEncoding.Utf16LE);
            string utf8c = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile(), UnicodeEncoding.Utf8);
            Assert.AreEqual(utf8, utf8a);
            Assert.AreEqual(utf8, utf8b);
            Assert.AreEqual(utf8, utf8c);

            // FileIO detects the actual UTF 16 LE encoding, ignores the provided flag
            string utf16lea = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile(), UnicodeEncoding.Utf16BE);
            string utf16leb = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile(), UnicodeEncoding.Utf16LE);
            string utf16lec = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile(), UnicodeEncoding.Utf8);
            Assert.AreEqual(utf8, utf16lea);
            Assert.AreEqual(utf8, utf16leb);
            Assert.AreEqual(utf8, utf16lec);

            // FileIO detects the actual UTF 16 BE encoding, ignores the provided flag
            string utf16bea = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile(), UnicodeEncoding.Utf16BE);
            string utf16beb = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile(), UnicodeEncoding.Utf16LE);
            string utf16bec = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile(), UnicodeEncoding.Utf8);
            Assert.AreEqual(utf8, utf16bea);
            Assert.AreEqual(utf8, utf16beb);
            Assert.AreEqual(utf8, utf16bec);

            // FileIO loads files without BOM when provided expected flag
            string utf8N = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf8.nobom.txt".ToFile(), UnicodeEncoding.Utf8);
            string utf16leN = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16le.nobom.txt".ToFile(), UnicodeEncoding.Utf16LE);
            string utf16beN = await FileIO.ReadTextAsync(await "ms-appdata:///temp/file1.utf16be.nobom.txt".ToFile(), UnicodeEncoding.Utf16BE);
            Assert.AreEqual(utf8, utf8N);
            Assert.AreEqual(utf8, utf16leN);
            Assert.AreEqual(utf8, utf16beN);
        }

        [TestMethod]
        public async Task ReadLinesWithEncodingAsyncTes()
        {
            // basic file reading text, in all encoding types
            IList<string> utf8 = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile());
            IList<string> utf16le = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile());
            IList<string> utf16be = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile());
            Assert.IsTrue(utf8.SequenceEqual(utf16le));
            Assert.IsTrue(utf8.SequenceEqual(utf16be));
        }

        [TestMethod]
        public async Task ReadLinesWithEncodingAsyncTest()
        {            // reference file
            IList<string> utf8 = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile());

            // FileIO detects the actual UTF8 encoding, ignores the provided flag
            IList<string> utf8a = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile(), UnicodeEncoding.Utf16BE);
            IList<string> utf8b = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile(), UnicodeEncoding.Utf16LE);
            IList<string> utf8c = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile(), UnicodeEncoding.Utf8);
            Assert.IsTrue(utf8.SequenceEqual(utf8a));
            Assert.IsTrue(utf8.SequenceEqual(utf8b));
            Assert.IsTrue(utf8.SequenceEqual(utf8c));

            // FileIO detects the actual UTF 16 LE encoding, ignores the provided flag
            IList<string> utf16lea = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile(), UnicodeEncoding.Utf16BE);
            IList<string> utf16leb = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile(), UnicodeEncoding.Utf16LE);
            IList<string> utf16lec = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile(), UnicodeEncoding.Utf8);
            Assert.IsTrue(utf8.SequenceEqual(utf16lea));
            Assert.IsTrue(utf8.SequenceEqual(utf16leb));
            Assert.IsTrue(utf8.SequenceEqual(utf16lec));

            // FileIO detects the actual UTF 16 BE encoding, ignores the provided flag
            IList<string> utf16bea = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile(), UnicodeEncoding.Utf16BE);
            IList<string> utf16beb = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile(), UnicodeEncoding.Utf16LE);
            IList<string> utf16bec = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile(), UnicodeEncoding.Utf8);
            Assert.IsTrue(utf8.SequenceEqual(utf16bea));
            Assert.IsTrue(utf8.SequenceEqual(utf16beb));
            Assert.IsTrue(utf8.SequenceEqual(utf16bec));

            // FileIO loads files without BOM when provided expected flag
            IList<string> utf8N = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf8.nobom.txt".ToFile(), UnicodeEncoding.Utf8);
            IList<string> utf16leN = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16le.nobom.txt".ToFile(), UnicodeEncoding.Utf16LE);
            IList<string> utf16beN = await FileIO.ReadLinesAsync(await "ms-appdata:///temp/file1.utf16be.nobom.txt".ToFile(), UnicodeEncoding.Utf16BE);
            Assert.IsTrue(utf8.SequenceEqual(utf8N));
            Assert.IsTrue(utf8.SequenceEqual(utf16leN));
            Assert.IsTrue(utf8.SequenceEqual(utf16beN));
        }

        [TestMethod]
        public async Task ReadBufferAsyncTest()
        {
            // basic file reading text, in all encoding types
            IBuffer butf8 = await FileIO.ReadBufferAsync(await "ms-appdata:///temp/file1.utf8.txt".ToFile());
            IBuffer butf16le = await FileIO.ReadBufferAsync(await "ms-appdata:///temp/file1.utf16le.txt".ToFile());
            IBuffer butf16be = await FileIO.ReadBufferAsync(await "ms-appdata:///temp/file1.utf16be.txt".ToFile());
            // write IBuffer to Stream and read it back using StreamReader
            string utf8 = await butf8.ReadBufferAsync();
            string utf16le = await butf16le.ReadBufferAsync();
            string utf16be = await butf16be.ReadBufferAsync();
            Assert.AreEqual(utf8, utf16le);
            Assert.AreEqual(utf8, utf16be);
        }

        private static readonly StorageFolder temp = ApplicationData.Current.TemporaryFolder;

        [TestMethod]
        public async Task WriteTextAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            string text = FileHelper.RandomMultilineText();
            await FileIO.WriteTextAsync(file, text);
            // read it back for test
            string text2 = await FileIO.ReadTextAsync(file);
            Assert.AreEqual(text, text2);
        }

        [TestMethod]
        public async Task WriteTextWithEncodingAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it in UTF8
            string text = FileHelper.RandomMultilineText();
            await FileIO.WriteTextAsync(file, text, UnicodeEncoding.Utf8);
            // read it back for test
            string text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf8);
            Assert.AreEqual(text, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it in UTF16LE
            text = FileHelper.RandomMultilineText();
            await FileIO.WriteTextAsync(file, text, UnicodeEncoding.Utf16LE);
            // read it back for test
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16LE);
            Assert.AreEqual(text, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it in UTF16BE
            text = FileHelper.RandomMultilineText();
            await FileIO.WriteTextAsync(file, text, UnicodeEncoding.Utf16BE);
            // read it back for test
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16BE);
            Assert.AreEqual(text, text2);
        }

        [TestMethod]
        public async Task AppendTextAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            string textA = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textA);
            string textB = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textB);
            string textC = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textC);
            // read it back for test
            string text2 = await FileIO.ReadTextAsync(file);
            Assert.AreEqual(textA + textB + textC, text2);
        }

        [TestMethod]
        public async Task AppendTextWithEncodingAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            string textA = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textA, UnicodeEncoding.Utf8);
            string textB = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textB, UnicodeEncoding.Utf8);
            string textC = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textC, UnicodeEncoding.Utf8);
            // read it back for test
            string text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf8);
            Assert.AreEqual(textA + textB + textC, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            textA = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textA, UnicodeEncoding.Utf16LE);
            textB = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textB, UnicodeEncoding.Utf16LE);
            textC = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textC, UnicodeEncoding.Utf16LE);
            // read it back for test
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16LE);
            Assert.AreEqual(textA + textB + textC, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            textA = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textA, UnicodeEncoding.Utf16BE);
            textB = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textB, UnicodeEncoding.Utf16BE);
            textC = FileHelper.RandomMultilineText();
            await FileIO.AppendTextAsync(file, textC, UnicodeEncoding.Utf16BE);
            // read it back for test
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16BE);
            Assert.AreEqual(textA + textB + textC, text2);
        }

        [TestMethod]
        public async Task WriteLinesAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            List<string> text = FileHelper.RandomMultilineList();
            await FileIO.WriteLinesAsync(file, text);
            // read it back for test
            string orig = string.Concat(text.Select(s => s + Environment.NewLine));
            string text2 = await FileIO.ReadTextAsync(file);
            Assert.AreEqual(orig, text2);
        }

        [TestMethod]
        public async Task WriteLinesWithEncodingAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            List<string> lines = FileHelper.RandomMultilineList();
            await FileIO.WriteLinesAsync(file, lines, UnicodeEncoding.Utf8);
            // read it back for test
            string orig = string.Concat(lines.Select(s => s + Environment.NewLine));
            string text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf8);
            Assert.AreEqual(orig, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            lines = FileHelper.RandomMultilineList();
            await FileIO.WriteLinesAsync(file, lines, UnicodeEncoding.Utf16LE);
            // read it back for test
            orig = string.Concat(lines.Select(s => s + Environment.NewLine));
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16LE);
            Assert.AreEqual(orig, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            lines = FileHelper.RandomMultilineList();
            await FileIO.WriteLinesAsync(file, lines, UnicodeEncoding.Utf16BE);
            // read it back for test
            orig = string.Concat(lines.Select(s => s + Environment.NewLine));
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16BE);
            Assert.AreEqual(orig, text2);
        }

        [TestMethod]
        public async Task AppendLinesAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            List<string> textA = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, textA);
            List<string> textB = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, textB);
            List<string> textC = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, textC);
            // read it back for test
            // PathIO adds after each appended line 'newline'
            string orig = string.Concat(textA.Concat(textB).Concat(textC).Select(s => s + Environment.NewLine));
            string text2 = await FileIO.ReadTextAsync(file);
            Assert.AreEqual(orig, text2);
        }

        [TestMethod]
        public async Task AppendLinesWithEncodingAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            List<string> linesA = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesA, UnicodeEncoding.Utf8);
            List<string> linesB = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesB, UnicodeEncoding.Utf8);
            List<string> linesC = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesC, UnicodeEncoding.Utf8);
            // read it back for test
            // PathIO adds after each appended line 'newline'
            string orig = string.Concat(linesA.Concat(linesB).Concat(linesC).Select(s => s + Environment.NewLine));
            string text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf8);
            Assert.AreEqual(orig, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            linesA = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesA, UnicodeEncoding.Utf16LE);
            linesB = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesB, UnicodeEncoding.Utf16LE);
            linesC = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesC, UnicodeEncoding.Utf16LE);
            // read it back for test
            // PathIO adds after each appended line 'newline'
            orig = string.Concat(linesA.Concat(linesB).Concat(linesC).Select(s => s + Environment.NewLine));
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16LE);
            Assert.AreEqual(orig, text2);

            // the target file must already exist when accessing via PathIO
            file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);
            // create random text and write it
            linesA = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesA, UnicodeEncoding.Utf16BE);
            linesB = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesB, UnicodeEncoding.Utf16BE);
            linesC = FileHelper.RandomMultilineList();
            await FileIO.AppendLinesAsync(file, linesC, UnicodeEncoding.Utf16BE);
            // read it back for test
            // PathIO adds after each appended line 'newline'
            orig = string.Concat(linesA.Concat(linesB).Concat(linesC).Select(s => s + Environment.NewLine));
            text2 = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf16BE);
            Assert.AreEqual(orig, text2);
        }

        [TestMethod]
        public async Task WriteBufferAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);

            byte[] bytes = FileHelper.RandomBytes(65536);
            // prepare buffer with random bytes
            IBuffer buffer = bytes.AsBuffer();
            // write the buffer to file
            await FileIO.WriteBufferAsync(file, buffer);
            // read the buffer back from file
            IBuffer buffer2 = await FileIO.ReadBufferAsync(file);
            byte[] bytes2 = buffer2.ToArray();
            Assert.IsTrue(bytes.SequenceEqual(bytes2));
        }

        [TestMethod]
        public async Task WriteBytesAsyncTest()
        {
            // the target file must already exist when accessing via PathIO
            StorageFile file = await temp.CreateFileAsync("test1", CreationCollisionOption.ReplaceExisting);

            byte[] bytes = FileHelper.RandomBytes(65536);
            // write the buffer to file
            await FileIO.WriteBytesAsync(file, bytes);
            // read the buffer back from file
            IBuffer buffer2 = await FileIO.ReadBufferAsync(file);
            byte[] bytes2 = buffer2.ToArray();
            Assert.IsTrue(bytes.SequenceEqual(bytes2));
        }
    }
}